/*
 * Copyright 2020-2025 the original author or authors.
 * You cannot use this file unless authorized by the author.
 */

package org.ipig.data.redis;

import org.ipig.commons.conf.redis.RedisConf;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoSerializable;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisException;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * RedisManager
 *
 * @author <a href="mailto:comchnts@163.com">chinats</a>
 * @version $$Id: RedisManager.java 3 2020-01-21 09:36:33Z ts <comchnts@163.com> $$
 * @since 1.0
 */
@Slf4j
@Data
public class RedisManager implements KryoSerializable, Serializable {
    /**
     * JedisPool
     */
    private volatile static JedisPool pool;
    /**
     * RedisConf
     */
    private RedisConf conf;

    public RedisManager() {
    }

    public RedisManager(RedisConf conf) {
        this.conf = conf;
        init();
    }


    /**
     * 初始化方法
     */
    private void init() {
        if (pool == null) {
            synchronized (RedisManager.class) {
                if (pool == null) {
                    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                    if (conf.getMaxTotal() != null) {
                        // 控制一个pool可分配多少个jedis实例，通过pool.getResource()来获取；
                        // 如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted。
                        jedisPoolConfig.setMaxTotal(conf.getMaxTotal());
                    }
                    if (conf.getMaxIdle() != null) {
                        // 控制一个pool最多有多少个状态为idle(空闲)的jedis实例；
                        jedisPoolConfig.setMaxIdle(conf.getMaxIdle());
                    }
                    if (conf.getMinIdle() != null) {
                        // 控制一个pool最少有多少个状态为idle(空闲)的jedis实例；
                        jedisPoolConfig.setMinIdle(conf.getMinIdle());
                    }
                    if (conf.getMaxWaitMillis() != null) {
                        // 表示当borrow一个jedis实例时，最大的等待时间，如果超过等待时间，则直接抛出JedisConnectionException；
                        jedisPoolConfig.setMaxWaitMillis(conf.getMaxWaitMillis());
                    }
                    if (conf.getTestOnBorrow() != null) {
                        // 在获取连接的时候检查有效性
                        jedisPoolConfig.setTestOnBorrow(conf.getTestOnBorrow());
                    }
                    int port = NumberUtils.toInt(conf.getPort());
                    pool = new JedisPool(jedisPoolConfig, conf.getHost(), port, conf.getDefaultTimeout(), conf.getPassword());
                    Runtime.getRuntime().addShutdownHook(new Thread() {
                        @Override
                        public void run() {
                            if (null != pool) {
                                pool.destroy();
                                pool = null;
                            }
                        }
                    });
                }
            }
        }
    }

    /**
     * 批量处理
     *
     * @param map
     * @param expireSeconds
     */
    public void setHashMap(HashMap<String, String> map, int expireSeconds) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            Pipeline pipe = cache.pipelined();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                pipe.set(entry.getKey(), entry.getValue());
            }
            if (pipe != null) {
                pipe.sync();
                pipe.close();
            }
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } catch (IOException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
    }

    /**
     * 缓存字符串
     *
     * @param key
     * @param value
     * @param dbIndex       数据库
     * @param expireSeconds 过期时间
     */
    public void setString(String key, String value, int dbIndex, int expireSeconds) {
        Jedis cache = null;
        try {
            cache = getResource(dbIndex);
            cache.set(key, value);
            cache.expire(key, expireSeconds);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
    }

    /**
     * 缓存字符串
     *
     * @param key
     * @param value
     * @param expireSeconds 过期时间
     */
    public void setString(String key, String value, int expireSeconds) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            cache.set(key, value);
            cache.expire(key, expireSeconds);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
    }

    /**
     * 通过key获取Value
     *
     * @param key     String
     * @param dbIndex int
     * @return String
     */
    public String getStringByKey(String key, int dbIndex) {
        Jedis cache = null;
        try {
            cache = getResource(dbIndex);
            return cache.get(key);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return null;
    }

    /**
     * 获取某个属性值
     *
     * @param key     key
     * @param hashKey hashKey
     * @return String
     */
    public String hashGet(String key, String hashKey) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            return cache.hget(key, hashKey);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return null;
    }

    /**
     * 获取某个KEY的所有信息
     *
     * @param key key
     * @return Map<String, String>
     */
    public Map<String, String> hashGetAll(String key) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            return cache.hgetAll(key);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return null;
    }

    /**
     * 设置属性值到缓存中
     *
     * @param key           key
     * @param hash          hash
     * @param expireSeconds expireSeconds
     * @return boolean
     */
    public boolean hashSet(String key, Map<String, String> hash, int expireSeconds) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            cache.hmset(key, hash);
            if (expireSeconds > 0) {
                cache.expire(key, expireSeconds);
            }
            return true;
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return false;
    }

    /**
     * 设置属性值到缓存中
     *
     * @param key           key
     * @param field         field
     * @param value         value
     * @param expireSeconds dbInexpireSecondsdex
     * @param dbIndex
     * @return boolean
     */
    public boolean hashSet(String key, String field, String value, int expireSeconds, int dbIndex) {
        Jedis cache = null;
        try {
            cache = getResource(dbIndex);
            cache.hset(key, field, value);
            if (expireSeconds > 0) {
                cache.expire(key, expireSeconds);
            }
            return true;
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return false;
    }

    /**
     * 设置属性值到缓存中
     *
     * @param key           key
     * @param value         value
     * @param expireSeconds expireSeconds
     * @return boolean
     */
    public boolean set(String key, String value, int expireSeconds) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            cache.set(key, value);
            if (expireSeconds > 0) {
                cache.expire(key, expireSeconds);
            }
            return true;
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return false;
    }

    /**
     * 从缓存中获取值
     *
     * @param key key
     * @return String
     */
    public String get(String key) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            return cache.get(key);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return null;
    }

    /**
     * 判断是否存在某个Key
     *
     * @param key key
     * @return boolean
     */
    public boolean exist(String key) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            return cache.exists(key);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return false;
    }

    /**
     * 判断是否存在某个Key
     *
     * @param key     key
     * @param field   field
     * @param dbIndex dbIndex
     * @return boolean
     */
    public boolean hexists(String key, String field, int dbIndex) {
        Jedis cache = null;
        try {
            cache = getResource(dbIndex);
            return cache.hexists(key, field);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return false;
    }

    /**
     * 删除某个key
     *
     * @param key key
     * @return boolean
     */
    public boolean delete(String key) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            cache.del(key);
            return true;
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return false;
    }

    /**
     * 删除某个key
     *
     * @param key     key
     * @param field   field
     * @param dbIndex dbIndex
     */
    public void hdel(String key, String field, int dbIndex) {
        Jedis cache = null;
        try {
            cache = getResource(dbIndex);
            cache.hdel(key, field);
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
    }

    /**
     * 获得某个key的值
     *
     * @param key key
     * @return Set<String>
     */
    public Set<String> getKeys(String key) {
        Jedis cache = null;
        try {
            cache = getDefaultResource();
            if (cache != null) {
                return cache.keys(key);
            }
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return null;
    }

    /**
     * lpush某个值到缓存到
     *
     * @param key   key
     * @param value value
     * @return long
     */
    public long lpush(String key, String value) {
        Jedis cache = null;
        long idx = -1;
        try {
            cache = getDefaultResource();
            if (cache != null) {
                idx = cache.lpush(key, value);
                return idx;
            }
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return idx;
    }

    /**
     * 弹出某个的key的值
     *
     * @param key key
     * @return String
     */
    public String lpop(String key) {
        Jedis cache = null;
        String result = null;
        try {
            cache = getDefaultResource();
            if (cache != null) {
                result = cache.lpop(key);
                return result;
            }
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return result;
    }

    /**
     * 弹出某个值到其他队列中
     *
     * @param source      source
     * @param destination destination
     * @param timeout     timeout
     * @return String
     */
    public String brpoplpush(String source, String destination, int timeout) {
        Jedis cache = null;
        String result = null;
        try {
            cache = getDefaultResource();
            if (cache != null) {
                result = cache.brpoplpush(source, destination, timeout);
                return result;
            }
        } catch (JedisException e) {
            log.error("进行缓存操作出现异常", e);
        } finally {
            if (cache != null) {
                cache.close();
            }
        }
        return result;
    }

    /**
     * get Default Resource
     *
     * @return Jedis
     */
    public Jedis getDefaultResource() {
        return getResource(this.conf.getDefaultDbIndex());
    }


    /**
     * get select Resource
     *
     * @param dbIndex int
     * @return Jedis
     */
    public Jedis getResource(int dbIndex) {
        Jedis jedis = pool.getResource();
        jedis.select(dbIndex);
        return jedis;
    }

    /**
     * 资源回收
     *
     * @param jedis
     */
    public void returnResource(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

    public void write(Kryo kryo, Output output) {
        kryo.writeObject(output, conf);
//        System.out.println("-----------------------序列化");
    }

    public void read(Kryo kryo, Input input) {
        conf = kryo.readObject(input, RedisConf.class);
//        System.out.println("-----------------------反序列化，同时构建JedisPool");
    }
}



